/*
 * @Author: laladuduqq
 * @Date: 2024-12-23 16:56:09
 * @LastEditors: laladuduqq
 * @LastEditTime: 2024-12-23 23:01:40
 */
#include "message.h"
#include "rtdef.h"
#include "rtthread.h"
#include "stdlib.h"
#include "string.h"

#define LOG_TAG              "message"
#define LOG_LVL              LOG_LVL_DBG
#include <ulog.h>

/* message_center是fake head node,是方便链表编写的技巧,这样就不需要处理链表头的特殊情况 */
static Publisher_t message_center = {
    .topic_name = "Message_Manager",
    .first_subs = NULL,
    .next_topic_node = NULL,
    .list = RT_LIST_OBJECT_INIT(message_center.list)};

static void CheckName(char *name)
{
    if (rt_strnlen(name, MAX_TOPIC_NAME_LEN + 1) >= MAX_TOPIC_NAME_LEN)
    {
        LOG_E("EVENT NAME TOO LONG:%s", name);
        RT_ASSERT(0)
    }
}

static void CheckLen(uint8_t len1, uint8_t len2)
{
    if (len1 != len2)
    {
        LOG_E("EVENT LEN NOT SAME:%d,%d", len1, len2);
        RT_ASSERT(0)
    }
}

Publisher_t *PubRegister(char *name, uint8_t data_len)
{
    CheckName(name);
    rt_list_t *list;
    rt_list_for_each(list, &message_center.list)
    {
        Publisher_t *node = rt_list_entry(list, Publisher_t, list);
        if (rt_strcmp(node->topic_name, name) == 0)
        {
            CheckLen(data_len, node->data_len);
            node->pub_registered_flag = 1;
            return node;
        }
    }
    // 遍历完发现尚未创建name对应的话题
    // 在链表尾部创建新的话题并初始化
    Publisher_t *node = (Publisher_t *)rt_malloc(sizeof(Publisher_t));
    rt_memset(node, 0, sizeof(Publisher_t));
    node->data_len = data_len;
    rt_strcpy(node->topic_name, name);
    node->pub_registered_flag = 1;
    rt_list_insert_after(&message_center.list, &node->list);
    return node;
}

Subscriber_t *SubRegister(char *name, uint8_t data_len)
{
    Publisher_t *pub = PubRegister(name, data_len); // 查找或创建该话题的发布者
    // 创建新的订阅者结点,申请内存,注意要memset因为新空间不一定是空的,可能有之前留存的垃圾值
    Subscriber_t *ret = (Subscriber_t *)rt_malloc(sizeof(Subscriber_t));
    rt_memset(ret, 0, sizeof(Subscriber_t));
    // 对新建的Subscriber进行初始化
    ret->data_len = data_len; // 设定数据长度
    ret->mq = rt_mq_create("mq", data_len, QUEUE_SIZE, RT_IPC_FLAG_PRIO);
    if (ret->mq == RT_NULL)
    {
        LOG_E("Failed to create message queue");
        rt_free(ret);
        return RT_NULL;
    }
    // 如果是第一个订阅者,特殊处理一下,将first_subs指针指向新建的订阅者(详见文档)
    if (pub->first_subs == NULL)
    {
        pub->first_subs = ret;
        return ret;
    }
    // 若该话题已经有订阅者, 遍历订阅者链表,直到到达尾部
    Subscriber_t *sub = pub->first_subs; // 作为iterator
    while (sub->next_subs_queue)         // 遍历订阅了该话题的订阅者链表
    {
        sub = sub->next_subs_queue; // 移动到下一个订阅者,遇到空指针停下,说明到了链表尾部
    }
    sub->next_subs_queue = ret; // 把刚刚创建的订阅者接上
    return ret;
}

/* 如果队列为空,会返回0;成功获取数据,返回1;后续可以做更多的修改,比如剩余消息数目等 */
uint8_t SubGetMessage(Subscriber_t *sub, void *data_ptr)
{
    rt_err_t result = rt_mq_recv(sub->mq, data_ptr, sub->data_len, RT_WAITING_NO);
    if (result == RT_EOK)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

uint8_t PubPushMessage(Publisher_t *pub, void *data_ptr)
{
    Subscriber_t *sub = pub->first_subs; // iter作为订阅者指针,遍历订阅该话题的所有订阅者;如果为空说明遍历结束
    // 遍历订阅了当前话题的所有订阅者,依次填入最新消息
    while (sub)
    {
        rt_mq_send(sub->mq, data_ptr, sub->data_len);
        sub = sub->next_subs_queue; // 访问下一个订阅者
    }
    return 1;
}